# selenium 4
from urllib.parse import urljoin

from prefect_dask import DaskTaskRunner
from selenium.webdriver import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
import yaml
import json
import pprint
import time
import uuid
import os
import re
from selenium.webdriver.support.wait import WebDriverWait

from utils import image_to_text
from selenium.webdriver.chrome.options import Options
from selenium import webdriver
from prefect import task, flow, serve


# 初始化selenium driver
def init_webdriver(config: object):
    options = Options()
    options.add_experimental_option("detach", True)
    options.add_argument("--start-maximized")
    options.add_argument('--no-sandbox')
    options.add_argument('--disable-dev-shm-usage')
    options.add_argument('"--headless"')
    # options.add_argument(r'user-data-dir=C:\Users\djs\AppData\Local\Temp\scoped_dir6276_796629387')
    # driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()), options=options)
    # driver = webdriver.Remote(command_executor='http://117.78.47.55:5000/wd/hub', options=options)
    driver = webdriver.Remote(command_executor=config['driver']['hub'], options=options)
    driver.implicitly_wait(config['driver']['timeout'])
    return driver


def load_config():
    with open(r'config/config.yaml', 'r', encoding='utf-8') as file:
        data = yaml.safe_load(file)
    # pprint.pprint(data)
    return data


# hvpp登录函数
@task
def login():
    # 获取配置文件
    config = load_config()
    # selenium driver 初始化
    driver = init_webdriver(config=config)
    # # selenium web 标准化
    web = WebDriverWait(driver, 10, 2)

    url = config['hvpp']['url']
    username = config['hvpp']['username']
    password = config['hvpp']['password']
    print(f"当前访问的url: {url}")
    driver.get(url)
    # username
    web.until(
        EC.visibility_of_element_located((By.XPATH, "//input[@placeholder='请输入账号']"))
    ).send_keys(username, Keys.TAB)
    # password
    web.until(
        EC.visibility_of_element_located((By.XPATH, "//input[@placeholder='请输入密码']"))
    ).send_keys(password, Keys.TAB)

    canvas_input = web.until(
        EC.visibility_of_element_located((By.XPATH, "//input[@placeholder='请输入验证码']"))
    )
    #  找到验证码
    canvas = web.until(
        EC.visibility_of_element_located((By.TAG_NAME, 'canvas'))
    )

    login_button = web.until(
        EC.visibility_of_element_located((By.XPATH, "//button/span[text()='登 录']"))
    )
    screenshots = os.path.join(os.path.dirname(__file__), 'screenshots')
    # 判断文件夹是否存在
    if not os.path.exists(screenshots):
        # 文件夹不存在，创建它
        os.makedirs(screenshots)
        print(f"文件夹 {screenshots} 已创建.")
    else:
        print(f"文件夹 {screenshots} 已存在.")

    # 如果识别的图片文字不是4位数，则重新生成
    while True:
        print('开始识别验证码')
        # 截图验证码
        filename = os.path.join(screenshots, str(uuid.uuid4()) + '.png')
        canvas.screenshot(filename)
        time.sleep(1)
        # 验证图片
        if os.path.exists(filename):
            img_txt = image_to_text(filename)
            print(img_txt)
            if len(img_txt) == 4:
                # 如果是4位数，尝试输入验证码
                # 验证码
                canvas_input.clear()
                canvas_input.send_keys(img_txt)
                login_button.click()
                time.sleep(1)

                if driver.execute_script("return window.localStorage.getItem('token');"):
                    # HiTMG = driver.execute_script("return window.localStorage.getItem('token');")
                    HiTMG = driver.execute_script("return window.localStorage.getItem('H-iTMG');")
                    break
        # 更新验证码
        canvas.click()

    # 删除文件夹
    if os.path.isdir(screenshots):
        # 使用 shutil.rmtree 删除整个目录及其内容
        import shutil
        shutil.rmtree(screenshots)
        print(f"文件夹 {screenshots} 已删除")
    else:
        print(f"文件夹 {screenshots} 不存在")

    # 站点token写入到本地文件中，方便后续读取使用
    with open('hvpp.txt', 'w', encoding='UTF-8') as f:
        f.write(HiTMG)
    driver.close()
    return HiTMG


# 资源管理界面投运资源
@task(retries=3)
def resource_archives():
    driver, config, web = init_job()

    # 返回结果集
    result = {'sum': 0, 'first_page': []}

    # 拼接url
    url = urljoin(config['hvpp']['url'], config['resource_archives']['path'])
    print(f"资源管理界面投运资源url: {url}")
    driver.get(url)

    print('开始计算投运项目数量')
    # 获取资源状态表头
    web.until(
        EC.visibility_of_element_located((By.XPATH, "//thead[@class='has-gutter']/tr//*[contains(text(), '资源状态')]"))
    ).click()
    time.sleep(0.4)
    web.until(
        EC.visibility_of_element_located(
            (By.XPATH, "//div[@class='el-table-filter']//div[@role='group']//span[contains(text(), '投运')]"))
    ).click()
    time.sleep(0.5)
    web.until(
        EC.presence_of_element_located((By.XPATH, "//div[@class='el-table-filter']//button[contains(text(), '筛选')]"))
    ).click()
    time.sleep(1)

    # 获取投运数量
    try:
        # 尝试获取设备总数
        total_str = web.until(EC.presence_of_element_located((By.XPATH, "//span[@class='el-pagination__total']"))).text
        # 过滤汉字，只保留数字
        pattern = '[\u4e00-\u9fa5]{0,}'
        total_num = int(re.sub(pattern, '', total_str))
        print('投运项目总数: {}'.format(total_num))
        print("截止目前，已并网项目数为{}个".format(total_num))
    except:
        print('没有投运的项目')

    result['sum'] = total_num

    thead = ['资源名称', '资源ID', '用电户号', '代理用户名称', '资源类型', '可调负荷(KW)', '资源类目', '资源状态',
             '额定功率', '关联设备数', ]

    # 获取表格
    tbodys = web.until(
        EC.visibility_of_all_elements_located(
            (By.XPATH,
             "//div[@id='app' and @class='vpp-project']//div[@class='HsDtkTable']/div[@class='hs-table-box']//table//tbody")
        )
    )
    val1 = tbodys[0].text.split('\n')
    val2 = tbodys[1].text.split('\n')

    # 如果大于10，只取第一页10条数据
    for i in range(10):
        cols = []
        cols.append(val2[i])
        for j in range(9):
            cols.append(val1[9 * i + j])
        result['first_page'].append(dict(zip(thead, cols)))
    #
    pprint.pprint(result)

    driver.close()
    return result


# 数据采集页面
@task(retries=3)
def dataStatistics():
    driver, config, web = init_job()

    # 返回结果集
    result = {'sum': 0, 'projects': []}

    # 拼接url
    url = urljoin(config['hvpp']['url'], config['dataStatistics']['path'])
    print(f"数据采集url: {url}")
    driver.get(url)
    print('开始巡检已并网项目数据采集状态')
    time.sleep(3)
    web.until(
        EC.visibility_of_element_located((By.XPATH, "//*[contains(text(), '投运中')]"))
    ).click()
    print('切换到投运中的页面')
    time.sleep(5)

    items = web.until(
        EC.visibility_of_all_elements_located(
            (By.XPATH,
             "//div[@class='tree-aside']//div[@role='tree']/div[@role='treeitem' and @tabindex='0']/div[@role='group']/div[@role='treeitem']"))
    )
    result['sum'] = len(items)
    print(f"投运总项目数：{len(items)}")

    for index, item in enumerate(items):
        temp = {
            'name': None,
            'status': None
        }
        # 二级标签的名字
        company = item.text.split('\n')[0]
        print(company)

        # 第一项默认是展开的，所以不需要单击，直接查看是否有存在漏采的情况
        if index == 0:
            title = web.until(EC.visibility_of_element_located(
                (By.XPATH, "//div[@id='app' and contains(@class,'vpp-project')]//div[@class='header-title']")
            )).text
            print(title)
            temp['name'] = title.split()[2]
            if '存在漏采' in title:
                temp['status'] = True
            else:
                temp['status'] = False
            result['projects'].append(temp)
            continue
        if index != 0:
            # 点击第一级
            item.click()
            time.sleep(0.5)
            # 点击第二级
            item.click()
            time.sleep(1)
            title = web.until(EC.visibility_of_element_located(
                (By.XPATH, "//div[@id='app' and contains(@class,'vpp-project')]//div[@class='header-title']")
            )).text
            # temp['name'] = company
            temp['name'] = title.split()[2]

            if '存在漏采' in title:
                temp['status'] = True
            else:
                temp['status'] = False
            result['projects'].append(temp)

            # 关闭二级标签
            web.until(
                EC.visibility_of_element_located(
                    (By.XPATH, f"//*[contains(text(), '{company}')]")
                )
            ).click()
        time.sleep(1)
    driver.close()
    pprint.pprint(result)
    return result


@task(retries=3)
def summary():
    driver, config, web = init_job()
    # 返回结果集
    result = {
        '设备总数': 0,
        '直连设备': 0,
        '直连网关': 0,
        '网关子设备': 0,
        '在线设备': 0,
        '离线设备': 0,
        '未激活设备': 0,
        '禁用设备': 0,
    }
    # 拼接url
    url = urljoin(config['hvpp']['url'], config['summary']['path'])
    driver.get(url)
    print(f"设备总览url: {url}")
    time.sleep(3)
    summary = web.until(EC.visibility_of_all_elements_located(
        (By.XPATH, "//div[@class='dev-indicador-item']/div[@class='cardIndicador']/div[@class='cardIndicadorRight']")))

    result['设备总数'] = summary[0].text.split()[-1]
    result['直连设备'] = summary[1].text.split()[-1]
    result['直连网关'] = summary[2].text.split()[-1]
    result['网关子设备'] = summary[3].text.split()[-1]
    # for item in summary:
    #     print(item.text)
    #     print(item.text.split('\n'))

    # detail = web.until(EC.visibility_of_all_elements_located(
    #     (By.XPATH, "//div[@id='app' and @class='ariot-app']//div[@class='chartLegend']")
    # ))
    detail = web.until(EC.visibility_of_all_elements_located(
        (By.XPATH, "//div[@id='app' and @class='ariot-app']//div[@class='chartLegend']//div[@class='legendContent']")
    ))
    result['在线设备'] = detail[0].text.split()[-2]
    result['离线设备'] = detail[1].text.split()[-2]
    result['未激活设备'] = detail[2].text.split()[-2]
    result['禁用设备'] = detail[3].text.split()[-2]

    # for item in detail:
    #     print(item.text.split())
    pprint.pprint(result)
    driver.close()
    return


@task(retries=3)
def alldevice(params: object):
    '''
    :param params : 来自数据采集里已已投运的项目
    :return devices: 返回各类状态的设备总数
    1、设备输入框输入设备名称
    2、点击表格状态筛选状态
    3、统计各个状态下的设备总数
    4、统计结果，写入到结果对象中
    '''
    pprint.pprint(params['projects'])
    driver, config, web = init_job()
    status = ['在线', '离线', '未激活']
    # 获取数据采集项目对应的设备名称
    devices = config['alldevice']['devices']

    # 拼接url
    url = urljoin(config['hvpp']['url'], config['alldevice']['path'])
    driver.get(url)
    print(f"设备管理url: {url}")

    # 设备输入框
    # 设备输入框的xpath 查找规则
    device_search = web.until(EC.visibility_of_element_located((By.XPATH, "//input[@placeholder='请输入搜索内容']")))
    time.sleep(2)

    # 找到状态的按钮
    status_button = web.until(
        EC.visibility_of_element_located(
            (By.XPATH, "//div[@class='hs-table-box']//thead[@class='has-gutter']//*[contains(text(), '状态')]")
        )
    )

    # 统计已并网了的每个项目，每种状态的设备总数
    for item in params['projects']:
        print(f"统计{item['name']}各状态的设备总数")
        device = devices[item['name']]
        print(device)

        device_search.clear()
        device_search.send_keys(device)
        device_search.send_keys(Keys.RETURN)
        time.sleep(3)

        # 查找各个状态下的设备总数
        for s in status:
            # 1、点击状态选择状态
            status_button.click()
            time.sleep(0.5)
            # 2、选择具体状态
            web.until(
                EC.visibility_of_element_located(
                    (By.XPATH, f"//div[@class='el-table-filter']//ul/li[contains(text(), '{s}')]")
                )
            ).click()
            # 等待筛选结果
            time.sleep(2)
            # 获取设备总数
            try:
                num_text = web.until(
                    EC.visibility_of_element_located(
                        (By.XPATH, "//div[contains(@class,'hs-table-pagination')]/span[@class='el-pagination__total']")
                    )
                ).text
                # 过滤汉字，只保留数字
                pattern = '[\u4e00-\u9fa5]{0,}'
                num = int(re.sub(pattern, '', num_text))
                item[f"{s}"] = num
                print(f"设备:{item['name']}-{s} 总数为{num}")
            except:
                print(f"设备:{item['name']}-{s} 总数为0")
                item[f"{s}"] = 0

            time.sleep(1)

        print(f"{item}")

    driver.close()
    pprint.pprint(params)
    return params


def groupdevice():
    driver, config, web = init_job()
    status = ['离线', '在线', '未激活']

    # 拼接url
    url = urljoin(config['hvpp']['url'], config['groupdevice']['path'])
    driver.get(url)
    print(f"分组管理url: {url}")

    result = {}

    # 找到状态的按钮
    status_button = web.until(
        EC.visibility_of_element_located(
            (By.XPATH, "//div[@class='hs-table-box']//thead[@class='has-gutter']//*[contains(text(), '状态')]")
        )
    )

    # 定位全部分组的元素
    root_group = web.until(
        EC.visibility_of_element_located(
            (By.XPATH, "//*[contains(@title, '全部分组')]")
        )
    )
    # 找到全部分组的父元素
    parent = root_group.find_element(By.XPATH, "..")
    # 找全部分组的所有同级资源
    brother = parent.find_elements(By.XPATH, "following-sibling::*")
    # 获取具体元素
    children = brother[0].find_elements(By.XPATH, "./div[@role='treeitem']")
    # 遍历每个具体元素
    for index, child in enumerate(children):
        spans = child.find_elements(By.XPATH, ".//span")
        # 点击分组
        spans[2].click()
        time.sleep(0.5)
        # 点击下拉框
        spans[0].click()
        contents = web.until(
            EC.visibility_of_all_elements_located(
                (By.XPATH,
                 f"//div[@class='main']//div[@role='tree']/div[@role='treeitem']/div[@role='group']/div[@role='treeitem'][{index + 1}]//span[@class='custom-tree-node']")
            )
        )
        # 如果结果大于1，所有下拉列表有新数据
        if len(contents) > 1:
            result[spans[2].text] = []
            # 遍历下拉框的所有子项
            for i in range(1, len(contents)):
                sub = {}
                sub['name'] = contents[i].text
                print(contents[i].text)
                # 点击具体子项
                contents[i].click()
                # 点击状态按钮选项
                status_button.click()
                # 获取不同状态的设备数量
                for s in status:
                    try:
                        web.until(
                            EC.visibility_of_element_located(
                                (By.XPATH, f"//div[@class='el-table-filter']//ul/li[contains(text(), '{s}')]")
                            )
                        ).click()
                        time.sleep(0.5)

                        # 获取设备总数

                        num_text = web.until(
                            EC.visibility_of_element_located(
                                (By.XPATH,
                                 "//div[contains(@class,'hs-table-pagination')]/span[@class='el-pagination__total']")
                            )
                        ).text
                        # 过滤汉字，只保留数字
                        pattern = '[\u4e00-\u9fa5]{0,}'
                        num = int(re.sub(pattern, '', num_text))
                        sub[s] = num
                        # item[f"{s}"] = num
                        # print(f"设备:{item['name']}-{s} 总数为{num}")
                    except:
                        sub[s] = 0
                        # print(f"设备:{item['name']}-{s} 总数为0")
                        # item[f"{s}"] = 0
                pprint.pprint(sub)
                result[spans[2].text].append(sub)
                time.sleep(1)
        time.sleep(0.5)
        # 点击下拉框，收缩下拉框
        spans[0].click()

    driver.close()
    pprint.pprint(result)
    return result


def init_job():
    # 获取配置文件
    config = load_config()
    # selenium driver 初始化
    driver = init_webdriver(config=config)
    # # selenium web 标准化
    web = WebDriverWait(driver, 10, 2)

    print("读取配置文件")
    # 读取hvpp的token文件
    with open('hvpp.txt', 'r', encoding='UTF-8') as f:
        HiTMG = f.read()

    HiTMG_obj = json.loads(HiTMG)
    token = HiTMG_obj['token']
    # 往浏览器里设置token 值
    if HiTMG:
        driver.get(config['hvpp']['url'])
        driver.execute_script(f"window.localStorage.setItem('H-iTMG', '{HiTMG}')")
        driver.execute_script(f"window.localStorage.setItem('token', '{token}')")
    return driver, config, web


@flow(log_prints=True)
def hvpp_base():
    login()


@flow(name="Hvpp Flow", log_prints=True, task_runner=DaskTaskRunner())
def hvpp_job():
    login_result = login.submit().result()
    resource_archives.submit()
    result = dataStatistics.submit()
    summary.submit()
    # result = dataStatistics()
    alldevice(result)


if __name__ == '__main__':
    # # hvpp 登录
    # HiTMG = login()
    # # # HiTMG_obj = json.loads(HiTMG)
    # # # print(HiTMG_obj['token'])
    # result_of_resource_archives = resource_archives()
    # pprint.pprint(result_of_resource_archives)
    # # print(f"资源管理投运数据结果, {result_of_resource_archives}")
    # dataStatistics()

    # flow_hvpp()
    # summary()
    # result = {'projects': [{'name': '天珑大厦空调机组', 'status': False},
    #                        {'name': '泰然商业', 'status': False},
    #                        {'name': '科发汽车智能楼宇空调资源', 'status': False},
    #                        {'name': '金中环', 'status': False},
    #                        {'name': '万科龙岗云中心', 'status': False},
    #                        {'name': '新一佳超市', 'status': False},
    #                        {'name': '新浩e都', 'status': True},
    #                        {'name': '松日鼎盛大厦', 'status': True},
    #                        {'name': '平安财险大厦', 'status': False},
    #                        {'name': '深圳市龙华区人民医院', 'status': False},
    #                        {'name': '盛景冰蓄冷空调YS', 'status': False},
    #                        {'name': '卓越世纪中心二期', 'status': False},
    #                        {'name': '英龙展业大厦', 'status': True},
    #                        {'name': '卓越城宝中时代广场', 'status': False},
    #                        {'name': '卓越时代广场', 'status': False},
    #                        {'name': '卓越后海中心', 'status': False}],
    #           'sum': 16}
    # # alldevice(params=result)
    # groupdevice()
    # flow_hvpp.serve(name='hvpp-deployment')
    # hvpp_auth_deploy = hvpp_base.to_deployment(name="hvpp-auth")
    # hvpp_job_deploy = hvpp_job.to_deployment(name="hvpp-job")
    # serve(hvpp_auth_deploy, hvpp_job_deploy)
    # serve(hvpp_job.to_deployment(name="hvpp-job"))
    # hvpp_job.deploy(
    #     name="docker-worker-test",
    #     work_pool_name="my-docker-pool",
    #     image="swr.cn-north-1.myhuaweicloud.com/htsd/hvpp-prefect:latest",
    #     push=True
    # )
    hvpp_job()